#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#define SERV_PORT 12345
#define MAXLINE 1024

// 错误处理函数，打印错误信息并退出程序
void error(int status, int errnum, const char *message) {
    fprintf(stderr, "%s: %s\n", message, strerror(errnum));
    exit(status);
}

// 创建TCP客户端套接字并连接到指定IP地址和端口的函数
int tcp_client(const char *ip, int port) {
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        error(1, errno, "socket error");
    }

    struct sockaddr_in servaddr;
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(port);
    if (inet_pton(AF_INET, ip, &servaddr.sin_addr) <= 0) {
        error(1, errno, "inet_pton error");
    }

    if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
        error(1, errno, "connect error");
    }

    return sockfd; // 可以对他读写数据了
}

int main(int argc, char **argv) {
    // 检查是否提供了正确数量的命令行参数
    if (argc != 2) {
        error(1, 0, "usage: select01 <IPaddress>");
    }
    // 创建TCP客户端套接字并连接服务器
    int client_fd = tcp_client(argv[1], SERV_PORT);

    // 声明接收和发送数据的缓冲区
    char recv_line[MAXLINE], send_line[MAXLINE];
    int n;

    // 声明用于读取的文件描述符集合及其备份
    fd_set readmask;
    fd_set allreads;
    // 初始化文件描述符集合并添加STDIN和socket_fd
    FD_ZERO(&allreads);
    FD_SET(STDIN_FILENO, &allreads); // 0标准输入
    FD_SET(client_fd, &allreads); // sock_fd

    // 处理接收到的数据和用户输入数据的主循环
    for (;;) {
        // 在调用select()之前复制文件描述符集合
        readmask = allreads;
        int rc = select(client_fd + 1, &readmask, NULL, NULL, NULL);

        // 处理select()失败的情况
        if (rc <= 0) {
            error(1, errno, "select failed");
        }

        // 处理从服务器接收到的数据
        if (FD_ISSET(client_fd, &readmask)) {
            n = read(client_fd, recv_line, MAXLINE);
            if (n < 0) {
                error(1, errno, "read error");
            } else if (n == 0) {
                error(1, 0, "server terminated \n");
            }
            // 将接收到的数据设为以空字符结尾并将其打印到stdout
            recv_line[n] = 0;
            fputs(recv_line, stdout);
            fputs("\n", stdout);
        }

        // 处理从用户输入（stdin）接收到的数据
        if (FD_ISSET(STDIN_FILENO, &readmask)) {
            if (fgets(send_line, MAXLINE, stdin) != NULL) {
                int i = strlen(send_line);
                if (send_line[i - 1] == '\n') {
                    send_line[i - 1] = 0;
                }

                // 将用户输入发送到服务器
                printf("now sending %s\n", send_line);
                size_t rt = write(client_fd, send_line, strlen(send_line));
                if (rt < 0) {
                    error(1, errno, "write failed ");
                }
                printf("send bytes: %zu \n", rt);
            }
        }
    }
}